/**
 * 统计数据缓存服务 - 简化版
 * 对长期数据(6个月以上)使用文件缓存，每天凌晨0点自动失效
 */

const fs = require('fs');
const path = require('path');
const { DateTime } = require('luxon');
const mysql = require('mysql2/promise');

// 创建数据库连接池
const pool = mysql.createPool({
  host: process.env.DB_HOST || '127.0.0.1',
  user: process.env.DB_USER || 'root',
  password: process.env.DB_PASSWORD || 'BinanceRoot2023',
  database: process.env.DB_NAME || 'binance_klines',
  port: process.env.DB_PORT || 3306,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// 需要缓存的长期数据范围
const CACHE_RANGES = ['6m', '1y', '2y', '3y', '4y', '5y', '6y', '7y'];

// 缓存文件目录
const CACHE_DIR = path.join(__dirname, '../../cache/stats');

// 确保缓存目录存在
if (!fs.existsSync(CACHE_DIR)) {
  fs.mkdirSync(CACHE_DIR, { recursive: true });
  console.log(`创建缓存目录: ${CACHE_DIR}`);
}

// 时间范围映射
const TIME_RANGE_MAPPING = {
  'today': { hours: 24 },
  'yesterday': { days: 1, hours: 24 },
  '1w': { weeks: 1 },
  '1m': { months: 1 },
  '3m': { months: 3 },
  '6m': { months: 6 },
  '1y': { years: 1 },
  '2y': { years: 2 },
  '3y': { years: 3 },
  '4y': { years: 4 },
  '5y': { years: 5 },
  '6y': { years: 6 },
  '7y': { years: 7 }
};

/**
 * 获取缓存文件路径
 * @param {string} symbol 交易对
 * @param {string} interval 时间间隔
 * @param {string} range 时间范围
 * @returns {string} 缓存文件路径
 */
function getCacheFilePath(symbol, interval, range) {
  return path.join(CACHE_DIR, `${symbol.toLowerCase()}_${interval}_${range}.json`);
}

/**
 * 检查缓存是否今天有效
 * @param {string} filePath 缓存文件路径
 * @returns {boolean} 缓存是否有效
 */
function isCacheValidToday(filePath) {
  if (!fs.existsSync(filePath)) {
    return false;
  }

  try {
    const stats = fs.statSync(filePath);
    const fileDate = new Date(stats.mtime);
    const now = new Date();
    
    // 如果文件的修改日期是今天，则缓存有效
    return fileDate.getDate() === now.getDate() && 
           fileDate.getMonth() === now.getMonth() && 
           fileDate.getFullYear() === now.getFullYear();
  } catch (error) {
    console.error(`检查缓存文件失败: ${error.message}`);
    return false;
  }
}

/**
 * 从缓存获取统计数据
 * @param {string} symbol 交易对
 * @param {string} interval 时间间隔
 * @param {string} range 时间范围
 * @returns {object|null} 统计数据
 */
function getStatsFromCache(symbol, interval, range) {
  // 只缓存长期数据
  if (!CACHE_RANGES.includes(range)) {
    console.log(`${symbol} ${interval} ${range} 不在缓存范围内，跳过缓存`);
    return null;
  }

  const cacheFilePath = getCacheFilePath(symbol, interval, range);
  console.log(`检查缓存文件: ${cacheFilePath}`);
  
  // 检查缓存是否今天有效
  if (!isCacheValidToday(cacheFilePath)) {
    console.log(`缓存不存在或已过期: ${symbol} ${interval} ${range}`);
    return null;
  }
  
  try {
    // 读取缓存文件
    const cacheContent = fs.readFileSync(cacheFilePath, 'utf8');
    const data = JSON.parse(cacheContent);
    
    console.log(`从缓存获取数据成功: ${symbol} ${interval} ${range}`);
    return data;
  } catch (error) {
    console.error(`从缓存获取数据失败: ${error.message}`);
    return null;
  }
}

/**
 * 将统计数据保存到缓存
 * @param {string} symbol 交易对
 * @param {string} interval 时间间隔
 * @param {string} range 时间范围
 * @param {object} data 统计数据
 */
function saveStatsToCache(symbol, interval, range, data) {
  // 只缓存长期数据
  if (!CACHE_RANGES.includes(range)) {
    return;
  }

  const cacheFilePath = getCacheFilePath(symbol, interval, range);
  
  try {
    // 保存缓存文件
    fs.writeFileSync(cacheFilePath, JSON.stringify(data, null, 2));
    console.log(`保存数据到缓存: ${symbol} ${interval} ${range}`);
  } catch (error) {
    console.error(`保存数据到缓存失败: ${error.message}`);
  }
}

/**
 * 计算统计数据
 * @param {string} symbol 交易对
 * @param {string} interval 时间间隔
 * @param {number} startTime 开始时间戳
 * @param {number} endTime 结束时间戳
 * @returns {object} 统计数据
 */
async function calculateStats(symbol, interval, startTime, endTime) {
  try {
    console.log(`开始计算统计数据: ${symbol}, ${interval}, 从 ${new Date(startTime).toLocaleString()} 到 ${new Date(endTime).toLocaleString()}`);
    
    const tableName = `kline_${interval}_enhanced`;
    
    // 查询总K线数量
    const [totalResult] = await pool.query(
      `SELECT COUNT(*) as total FROM ${tableName} 
       WHERE symbol = ? AND open_time BETWEEN ? AND ?`,
      [symbol, startTime, endTime]
    );
    
    // 查询上涨K线数量
    const [upResult] = await pool.query(
      `SELECT COUNT(*) as up FROM ${tableName} 
       WHERE symbol = ? AND open_time BETWEEN ? AND ? 
       AND close_price > open_price`,
      [symbol, startTime, endTime]
    );
    
    // 查询下跌K线数量
    const [downResult] = await pool.query(
      `SELECT COUNT(*) as down FROM ${tableName} 
       WHERE symbol = ? AND open_time BETWEEN ? AND ? 
       AND close_price < open_price`,
      [symbol, startTime, endTime]
    );
    
    // 查询平盘K线数量
    const [flatResult] = await pool.query(
      `SELECT COUNT(*) as flat FROM ${tableName} 
       WHERE symbol = ? AND open_time BETWEEN ? AND ? 
       AND close_price = open_price`,
      [symbol, startTime, endTime]
    );

    // 获取总K线数量
    const totalKlines = totalResult[0].total;
    const upKlines = upResult[0].up;
    const downKlines = downResult[0].down;
    const flatKlines = flatResult[0].flat;

    console.log(`统计结果: 总计:${totalKlines}, 上涨:${upKlines}, 下跌:${downKlines}, 平:${flatKlines}`);

    // 使用SQL查询获取连续上涨数据
    const [upStreakResults] = await pool.query(
      `SELECT 
        streak_length AS count,
        COUNT(*) AS occurrences
      FROM (
        SELECT 
          group_num,
          COUNT(*) AS streak_length
        FROM (
          SELECT 
            @rn := @rn + 1 AS row_id,
            @streak := IF(@prev_symbol = BINARY symbol AND @prev_close < close_price, 
                        @streak + 1, 
                        IF(@prev_symbol = BINARY symbol AND @prev_close >= close_price, 0, 1)
                      ) AS is_up,
            @group_num := IF(@prev_symbol = BINARY symbol AND @prev_close < close_price, 
                          @group_num, 
                          @rn
                        ) AS group_num,
            @prev_symbol := symbol AS prev_symbol, 
            @prev_close := close_price AS prev_close
          FROM ${tableName}, 
            (SELECT @rn := 0, @streak := 0, @group_num := 0, @prev_symbol := '', @prev_close := 0) AS vars
          WHERE symbol = ? 
            AND open_time BETWEEN ? AND ?
          ORDER BY symbol, open_time
        ) AS streak_data
        WHERE is_up > 0
        GROUP BY group_num
        HAVING COUNT(*) >= 2
      ) AS streak_groups
      GROUP BY streak_length`,
      [symbol, startTime, endTime]
    );

    // 使用SQL查询获取连续下跌数据
    const [downStreakResults] = await pool.query(
      `SELECT 
        streak_length AS count,
        COUNT(*) AS occurrences
      FROM (
        SELECT 
          group_num,
          COUNT(*) AS streak_length
        FROM (
          SELECT 
            @rn := @rn + 1 AS row_id,
            @streak := IF(@prev_symbol = BINARY symbol AND @prev_close > close_price, 
                        @streak + 1, 
                        IF(@prev_symbol = BINARY symbol AND @prev_close <= close_price, 0, 1)
                      ) AS is_down,
            @group_num := IF(@prev_symbol = BINARY symbol AND @prev_close > close_price, 
                          @group_num, 
                          @rn
                        ) AS group_num,
            @prev_symbol := symbol AS prev_symbol, 
            @prev_close := close_price AS prev_close
          FROM ${tableName}, 
            (SELECT @rn := 0, @streak := 0, @group_num := 0, @prev_symbol := '', @prev_close := 0) AS vars
          WHERE symbol = ? 
            AND open_time BETWEEN ? AND ?
          ORDER BY symbol, open_time
        ) AS streak_data
        WHERE is_down > 0
        GROUP BY group_num
        HAVING COUNT(*) >= 2
      ) AS streak_groups
      GROUP BY streak_length`,
      [symbol, startTime, endTime]
    );

    // 将结果转换为对象格式
    const consecutiveUp = {};
    const consecutiveDown = {};

    upStreakResults.forEach(row => {
      consecutiveUp[row.count] = row.occurrences;
    });

    downStreakResults.forEach(row => {
      consecutiveDown[row.count] = row.occurrences;
    });

    console.log(`完成统计数据计算: ${symbol}, ${interval}`);
    
    return {
      total: totalKlines,
      up: upKlines,
      down: downKlines,
      flat: flatKlines,
      consecutive: {
        up: consecutiveUp,
        down: consecutiveDown
      },
      timeRange: {
        start: new Date(startTime).toISOString(),
        end: new Date(endTime).toISOString()
      }
    };
  } catch (error) {
    console.error(`计算统计数据失败: ${error.message}`, error.stack);
    throw new Error(`计算统计数据失败: ${error.message}`);
  }
}

/**
 * 获取统计数据（先尝试从缓存获取，如果不存在则计算并更新缓存）
 * @param {string} symbol 交易对
 * @param {string} interval 时间间隔
 * @param {string} range 时间范围
 * @returns {object} 统计数据
 */
async function getStats(symbol, interval, range) {
  console.log(`获取统计数据: ${symbol} ${interval} ${range}`);
  
  // 尝试从缓存获取（仅长期数据）
  let stats = getStatsFromCache(symbol, interval, range);
  
  // 如果缓存不存在或已过期，则计算并更新缓存
  if (!stats) {
    console.log(`缓存不存在或已过期，计算统计数据: ${symbol} ${interval} ${range}`);
    
    // 使用系统当前时间作为基准
    const now = DateTime.now().setZone('Asia/Hong_Kong');
    console.log(`当前系统时间: ${now.toFormat('yyyy-MM-dd HH:mm:ss')}`);
    
    let startTime, endTime;
    
    if (range === 'today') {
      // 今天的数据，从当天0点开始
      startTime = now.startOf('day').toMillis();
      endTime = now.toMillis();
    } else if (range === 'yesterday') {
      // 昨天的数据，从昨天0点到今天0点
      startTime = now.minus({ days: 1 }).startOf('day').toMillis();
      endTime = now.startOf('day').toMillis();
    } else {
      // 其他时间范围，根据配置计算
      const duration = TIME_RANGE_MAPPING[range];
      startTime = now.minus(duration).toMillis();
      endTime = now.toMillis();
      
      // 确保时间范围正确显示在前端
      if (range && ['4y', '5y', '6y', '7y'].includes(range)) {
        console.log(`处理${range}时间范围，确保开始时间计算正确`);
        console.log(`开始时间: ${new Date(startTime).toLocaleString()}`);
      }
    }
    
    console.log(`时间范围: ${new Date(startTime).toLocaleString()} 至 ${new Date(endTime).toLocaleString()}`);
    
    // 计算统计数据
    stats = await calculateStats(symbol, interval, startTime, endTime);
    
    // 保存到缓存（仅长期数据）
    saveStatsToCache(symbol, interval, range, stats);
  }
  
  return stats;
}

module.exports = {
  getStats,
  getStatsFromCache,
  calculateStats
}; 